<?php
/**
 * Ganglib
 *
 * LICENSE
 *
 * This source file is subject to the GNU Lesser Generak Public license.
 * 
 * It is available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/lgpl.html
 *
 * @category    Ganglib
 * @package    Div_Db
 * @copyright  Copyright (c) 2008 Ganglib project members (http://www.code.google.com/p/ganglib)
 * @license     http://www.gnu.org/licenses/lgpl.html
 */

/**
 * Database queries distributor. Query distributor send created queries to data sources using
 * Strategies to select valid data-source. All data source specific operations are implemented 
 * in specific strategies, so Database Distributor is quite independent. Additionaly it caches
 * all results of data operations using Cache Manager.
 * 
 * @package    Div_Db
 * @copyright  Copyright (c) 2008 Ganglib project members (http://www.code.google.com/p/ganglib)
 * @license     http://www.gnu.org/licenses/lgpl.html 
 *
 */
class Div_Db_Distributor
{
	/**
	 * Self Instance
	 *
	 * @var Div_Db_Distributor
	 */
	static private $instance;
	
	/**
	 * Configuration
	 *
	 * @var Zend_Config
	 */
	protected $_config;
	
	/**
	 * Strategy
	 *
	 * @var Div_Db_Distributor_Strategy
	 */
	protected $_strategy;
	
	/**
	 * Zend_Cache object
	 *
	 * @var Zend_Cache
	 */
	protected $_cache;
	
	/**
	 * Block construct
	 *
	 * @access private
	 */
	private function __construct()
	{
		
	}
	
	/**
	 * Block object cloning
	 *
	 * @access private
	 */
	private function __clone()
	{
		
	}
	
	/**
	 * Gets singleton object instance
	 *
	 * @access public
	 * @return  Div_Db_Distributor
	 */
	public static function getInstance()
	{
		if(!self::$instance)
			self::$instance = new Div_Db_Distributor();
		
		return self::$instance;
	}
	
	/**
	 * Set config
	 *
	 * @param Zend_Config $config
	 * @return Div_Db_Distributor
	 */
	public function setConfig($config = null)
	{
		if($config)
			$this->_config = $config;
		return $this;
	}
	
	/**
	 * Set strategy
	 *
	 * @param object $strategy
	 * @return Div_Db_Distributor
	 */
	public function setStrategy($strategy)
	{
		$strategyClass = 'Div_Db_Distributor_Strategy_'.$strategy;
		require_once str_replace('_', DIRECTORY_SEPARATOR, $strategyClass) . '.php';
		
		$this->_strategy = new $strategyClass($this->_config);
		
		return $this;
	}
	
	/**
	 * Set cache object
	 *
	 * @param Zend_Cache $cache
	 * @return Div_Db_Distributor
	 */
	public function setCache(Zend_Cache_Core $cache)
	{
		$this->_cache = $cache;
		return $this;
	}
	
	/**
	 * Read query results - from cache or strategy
	 *
	 * @param Div_Db_Query $query
	 * @param Zend_Cache_Core $cache
	 * @param string $cache_key user-defined cache key. By Default cache key= sha1($query)
	 * @return unknown
	 */
	public function query(Div_Db_Query $query, Zend_Cache_Core $cache = null, $cache_key = null)
	{ 
		// set proper cache
		$cache = $cache ? $cache : $this->_cache;
		if($cache && ($result = $this->_getCached($query, $cache, $cache_key)))
			return $result;
			
		// load Strategy object
		if(!$this->_strategy)
			self::throwException('You must set strategy before query');
//			require_once('Div/Helpers/log.php');
//			log::message($cache_key.' - '.$query);
			
		$result = $this->_strategy->execute($query);
	
		if($cache)
			$result = $this->_setCache($query, $result, $cache, $cache_key);

		return $result;
	}
	
	/**
	 * Get cached query
	 *
	 * @param Div_Db_Query $query
	 * @param Zend_Cache $cache
	 * @param string $hash
	 * @return mixed
	 * @access pivate
	 */
	private function _getCached(Div_Db_Query $query, Zend_Cache_Core $cache, $hash = null)
	{
		if(!$hash)
			$hash = sha1($query);

		if($result = $cache->load($hash))
			return $result;
		
		return false;
	}
	
	/**
	 * Set cache with result from db
	 *
	 * @param Div_Db_Query $query
	 * @param PDO_Statement $result
	 * @param Zend_Cache_Core $cache
	 * @param string $hash 
	 * @return PDO_Statement
	 * @access private
	 */
	private function _setCache(Div_Db_Query $query, $result, Zend_Cache_Core $cache, $hash = null)
	{
		if(!$hash)
			$hash = sha1($query);
			
		if($this->_strategy->isCacheable)
		{
			//log::message('save cache'. (print_r($result, true)).$hash);
			$cache->save($result, $hash);
		}
		
		return $result;
	}
	
    /**
     * Throw an exception
     *
     * Note : for perf reasons, the "load" of Zend/Cache/Exception is dynamic
     * @param  string $msg  Message for the exception
     * @throws Zend_Cache_Exception
     */
    public static function throwException($msg)
    {
        // For perfs reasons, we use this dynamic inclusion
        require_once 'Div/Db/Distributor/Exception.php';
        throw new Div_Db_Distributor_Exception($msg);
    }
}